/* ===== semantic.c ===== */
#include <string>
#include <iostream>
#include <map>
#include <list>
#include <vector>

using namespace std;

#include <stdio.h>
#include <stdlib.h>

#include "ptype.hh"
#include "symtab.hh"
#include "codegest.hh"

#include "myASTnode.hh"

#include "util.hh"
#include "semantic.hh"

#include "codegen.hh"

// symbol table with information about identifiers in the program
// declared in symtab.cc
extern symtab symboltable;

// When dealing with a list of instructions, it contains the maximum auxiliar space
// needed by any of the instructions for keeping non-referenceable non-basic-type values.
int maxoffsetauxspace;

// When dealing with one instruction, it contains the auxiliar space
// needed for keeping non-referenceable values.
int offsetauxspace;

// For distinghishing different labels for different if's and while's.
int newLabelWhile(bool inicialitzar = false){
  static int comptador = 1;
  if (inicialitzar) comptador = 0;
  return comptador++;
}

int newLabelIf(bool inicialitzar = false){
  static int comptador = 1;
  if (inicialitzar) comptador = 0;
  return comptador++;
}


codechain indirections(int jumped_scopes,int t)
{
  codechain c;
  if (jumped_scopes==0) {
    c="aload static_link t"+itostring(t);
  }
  else {
    c="load static_link t"+itostring(t);
    for (int i=1;i<jumped_scopes;i++) {
      c=c||"load t"+itostring(t)+" t"+itostring(t);
    }
  }
  return c;
}

int compute_size(ptype tp)
{
  if (isbasickind(tp->kind)) {
    tp->size=4;
  }
  else if (tp->kind=="array") {
    tp->size=tp->numelemsarray*compute_size(tp->down);
  }
  else if (tp->kind=="struct") {
    tp->size=0;
    for (list<string>::iterator it=tp->ids.begin();it!=tp->ids.end();it++) {
      tp->offset[*it]=tp->size;
      tp->size+=compute_size(tp->struct_field[*it]);
    }
  }
  return tp->size;
}

void gencodevariablesandsetsizes(scope *sc,codesubroutine &cs,bool isfunction=0)
{
  if (isfunction) cs.parameters.push_back("returnvalue");
  for (list<string>::iterator it=sc->ids.begin();it!=sc->ids.end();it++) {
    if (sc->m[*it].kind=="idvarlocal") {
      variable_data vd;
      vd.name="_"+(*it);
      vd.size=compute_size(sc->m[*it].tp);
      cs.localvariables.push_back(vd);
    } else if (sc->m[*it].kind=="idparval" || sc->m[*it].kind=="idparref") {
      compute_size(sc->m[*it].tp);
      cs.parameters.push_back("_"+(*it));
    } else if (sc->m[*it].kind=="idfunc") {
      // Here it is assumed that in tp->right is kept the return value type
      // for the case of functions. If this is not the case you must
      // change this line conveniently in order to force the computation
      // of the size of the type returned by the function.
      compute_size(sc->m[*it].tp->right);
    } else if (sc->m[*it].kind=="idproc") {
      // Nothing to be done.
    }
    //cout << sc->m[*it].kind << endl;
    
  }
  cs.parameters.push_back("static_link");
}

codechain GenLeft(AST *a,int t);
codechain GenRight(AST *a,int t);

void CodeGenRealParams(AST *a,ptype tp,codechain &cpushparam,codechain &cremoveparam,int t)
{
  if (!a) return;
  //cout<<"Starting with node \""<<a->kind<<"\""<<endl;

  //...to be done.
  
	while(a != NULL) {
		if(a->kind == "ident"){
			if(tp->kind == "parref")
		 		cpushparam = cpushparam || GenLeft(a,t) || "pushparam t"+itostring(t); 
		 	else if(tp->kind == "parval")
		 		cpushparam = cpushparam || GenRight(a,t) || "pushparam t"+itostring(t); 
		 }
		else cpushparam = cpushparam || GenRight(a,t) || "pushparam t"+itostring(t); 
		cremoveparam = cremoveparam || "killparam";
		a = a->right;
		tp = tp->right;
	}
  
  

  //cout<<"Ending with node \""<<a->kind<<"\""<<endl;
}

// ...to be completed:
codechain GenLeft(AST *a,int t)
{
  codechain c;

  if (!a) {
    return c;
  }

  //cout<<"Starting with node \""<<a->kind<<"\""<<endl;
  if (a->kind=="ident") {
  	//cout << a->text << endl;
     //cout << symboltable[a->text].kind << endl;    
    //comprovem si la variable es troba a l'àmbit actual o en un àmbit superior
    if(symboltable.jumped_scopes(a->text)==0){
    	if(symboltable[a->text].kind == "idparref"){
    	 	c="load _"+a->text+" t"+itostring(t);
    	}
		else if(symboltable[a->text].kind == "idparval"){
			if(isbasickind(a->tp->kind))c="aload _"+a->text+" t"+itostring(t);
			else c="load _"+a->text+" t"+itostring(t);
		}
		else c="aload _"+a->text+" t"+itostring(t);
    }
    else {
    	//if(symboltable[a->text].kind == "idvarlocal")c="aload _"+a->text+" t"+itostring(t);
    	//else {
    		/*c = "load static_link t"+itostring(t);
    		int i = symboltable.jumped_scopes(a->text) - 1;
    		while (i>0) {
    			c = c || "load t" + itostring(t) + " t" + itostring(t);
    			i--;
    		}*/
    		/*c = indirections(symboltable.jumped_scopes(a->text),t) ||
    		"addi t" + itostring(t) + " offset(" + symboltable.idtable(a->text) + ":_" + a->text + ") t" + itostring(t);
            if(symboltable[a->text].kind == "idparref")c = c ||"load t" + itostring(t) + " t" + itostring(t);*/
    	//}
    	
    	
    	if(symboltable[a->text].kind == "idparref") {
    		c = "load static_link t" + itostring(t) ||
    			"addi t" + itostring(t) + " offset(" + symboltable.idtable(a->text) + ":_" + a->text + ") t" + itostring(t) ||
    			"load t" + itostring(t) + " t" + itostring(t);
    	}
    	else if(symboltable[a->text].kind == "idparval") {
    		c = "load static_link t" + itostring(t);
    		for(int i = 1; i < symboltable.jumped_scopes(a->text); i++) {
    			c = c || "load t" + itostring(t) + " t" + itostring(t);
    		}
    		c = c || "addi t" + itostring(t) + " offset(" + symboltable.idtable(a->text) + ":_" + a->text + ") t" + itostring(t);
    	}
    	else if(symboltable[a->text].kind != "idparval" && symboltable[a->text].kind != "idparval"){
    		c = "load static_link t" + itostring(t);
    		for(int i = 1; i < symboltable.jumped_scopes(a->text); i++) {
    			c = c || "load t" + itostring(t) + " t" + itostring(t);
    		}
    		c = c || "addi t" + itostring(t) + " offset(" + symboltable.idtable(a->text) + ":_" + a->text + ") t" + itostring(t);
    	}
    	
    }
  }
  else if (a->kind=="."){
    c=GenLeft(child(a,0),t)||
      "addi t"+itostring(t)+" "+
      itostring(child(a,0)->tp->offset[child(a,1)->text])+" t"+itostring(t);
  }
/*-------------------------------------------------------------------*/
else if(a->kind=="[") {
	if(child(a,1)->kind == "." || child(a,1)->kind == "[") {
		c=GenLeft(child(a,0),t) ||
		GenRight(child(a,1),t+1) ||
		"muli t"+itostring(t+1)+" "+
		itostring((child(a,0)->tp->size)/(child(a,0)->tp->numelemsarray))+" t"+itostring(t+1)||
		"addi t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
		
	}
	else {
		c=GenLeft(child(a,0),t) || 
		  GenRight(child(a,1),t+1) ||
		"muli t"+itostring(t+1)+" "+
		itostring((child(a,0)->tp->size)/(child(a,0)->tp->numelemsarray))+" t"+itostring(t+1)||
		"addi t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
		
	}
}
/*-------------------------------------------------------------------*/
  else {
    cout<<"BIG PROBLEM! No case defined for kind "<<a->kind<<endl;
  }
  //cout<<"Ending with node \""<<a->kind<<"\""<<endl;
  return c;
}


// ...to be completed:
codechain GenRight(AST *a,int t)
{
  codechain c;

  if (!a) {
    return c;
  }

  //cout<<"Starting with node \""<<a->kind<<"\""<<endl;
  if (a->ref) {
    if (a->kind=="ident" && symboltable.jumped_scopes(a->text)==0 &&
	isbasickind(symboltable[a->text].tp->kind) && symboltable[a->text].kind!="idparref") {
		c="load _"+a->text+" t"+itostring(t);
    }
    else if (isbasickind(a->tp->kind)) {
      c = GenLeft(a,t)||"load t"+itostring(t)+" t"+itostring(t);
    }
    else {//...to be done
    	c = GenLeft(a,t+1) ||
    		"aload aux_space t" + itostring(t) ||
    		"addi t" + itostring(t) +" " +itostring(offsetauxspace) + " t" + itostring(t) ||
    		"copy t" + itostring(t+1) + " t" + itostring(t) + " " + itostring(a->tp->size);
    		
    	offsetauxspace += a->tp->size;
    }    
  } 
/*-------------------------------------------------------------------*/
  else if (a->kind=="intconst") {
    c="iload "+a->text+" t"+itostring(t);
  }
/*-------------------------------------------------------------------*/
  else if (a->kind == "true") {
	c = "iload 1 t"+itostring(t);
  }
/*-------------------------------------------------------------------*/
  else if (a->kind == "false") {
	c = "iload 0 t"+itostring(t);
  }
/*-------------------------------------------------------------------*/
  else if (a->kind=="+") {
    c=GenRight(child(a,0),t)||
      GenRight(child(a,1),t+1)||
      "addi t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
  }
/*-------------------------------------------------------------------*/
  else if (a->kind=="-" && child(a,1)!=0) {
    c=GenRight(child(a,0),t)||
      GenRight(child(a,1),t+1)||
      "subi t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
  }
/*-------------------------------------------------------------------*/
  else if (a->kind=="*") {
    c=GenRight(child(a,0),t)||
      GenRight(child(a,1),t+1)||
      "muli t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
  }
/*-------------------------------------------------------------------*/
  else if (a->kind=="/") {
    c=GenRight(child(a,0),t)||
      GenRight(child(a,1),t+1)||
      "divi t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
  }
/*-------------------------------------------------------------------*/
else if(a->kind == "-"){
	c=GenRight(child(a,0),t) ||
	"mini t"+itostring(t)+" t"+itostring(t);
}
/*-------------------------------------------------------------------*/
else if(a->kind == "and"){
    c=GenRight(child(a,0),t)||
      GenRight(child(a,1),t+1)||
      "land t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
}
/*-------------------------------------------------------------------*/
else if(a->kind == "or"){
    c=GenRight(child(a,0),t)||
      GenRight(child(a,1),t+1)||
      "loor t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
}
/*-------------------------------------------------------------------*/

else if(a->kind == "not"){
	c=GenRight(child(a,0),t) ||
	"lnot t"+itostring(t)+" t"+itostring(t);
}
/*-------------------------------------------------------------------*/
else if(a->kind == ">"){
    c=GenRight(child(a,0),t)||
      GenRight(child(a,1),t+1)||
      "grti t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
}
/*-------------------------------------------------------------------*/
else if(a->kind == "<"){
    c=GenRight(child(a,0),t)||
      GenRight(child(a,1),t+1)||
      "lesi t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
}
/*-------------------------------------------------------------------*/
else if(a->kind == "=") {
	c=GenRight(child(a,0),t)||
	GenRight(child(a,1),t+1)||
	"equi t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
	
	
}
/*-------------------------------------------------------------------*/
else if(a->kind == "(") {
	
	AST *aux = child(child(a,1),0);
	int i = 1;
 	ptype tp = symboltable[child(a,0)->text].tp;
 	ptype tp2 = tp;
 	codechain cpushparam;
 	codechain cremoveparam;
 	int taux;
 	
	if(child(a,0)->tp->kind == "function"){
		if(isbasickind(tp2->right->kind)) c = "pushparam 0";
		else {
			c =   "aload aux_space t" + itostring(t)
				  || "addi t" + itostring(t) +" " +itostring(offsetauxspace) +" t" + itostring(t)
				  || "pushparam t" + itostring(t);
		 	taux = t;
			++t;
			offsetauxspace += compute_size(tp2->right);
		}
		
	}
 	tp = tp->down;
	CodeGenRealParams(aux,tp,cpushparam,cremoveparam,t);
	c = c || cpushparam || indirections(symboltable.jumped_scopes(child(a,0)->text),t) || "pushparam t" + itostring(t)
		|| "call program_" + child(a,0)->text || "killparam"
		|| cremoveparam;

	if(child(a,0)->tp->kind == "function") {
		if(isbasickind(tp2->right->kind)) c = c || "popparam t" + itostring(t);
		else {
			c = c || "killparam";
		}

	}
	
}

/*-------------------------------------------------------------------*/

  else {
    cout<<"BIG PROBLEM! No case defined for kind "<<a->kind<<endl;
  }
  //cout<<"Ending with node \""<<a->kind<<"\""<<endl;
  return c;
}


// ...to be completed:
codechain CodeGenInstruction(AST *a,string info="")
{
  codechain c;

  if (!a) {
    return c;
  }
  //cout<<"Starting with node \""<<a->kind<<"\""<<endl;
  offsetauxspace=0;
  if (a->kind=="list") {
    for (AST *a1=a->down;a1!=0;a1=a1->right) {
      c=c||CodeGenInstruction(a1,info);
    }
  }
/*-------------------------------------------------------------------*/
  else if (a->kind==":=") {
      	//cout << child(a,1)->kind << endl;
    if (isbasickind(child(a,0)->tp->kind)) {
    	/*if(child(a,1)->kind == "(") c=GenLeft(child(a,0),0)||CodeGenInstruction(child(a,1))||"stor t1 t0";
        else c=GenLeft(child(a,0),0)||GenRight(child(a,1),1)||"stor t1 t0";//GenLeft y Right generen codechains*/
        c=GenLeft(child(a,0),0)||GenRight(child(a,1),1)||"stor t1 t0";//GenLeft y Right generen codechains
        
    }
    else if (child(a,1)->ref) {
    	    //cout << child(a,1)->kind << endl;
      c=GenLeft(child(a,0),0)||GenLeft(child(a,1),1)||"copy t1 t0 "+itostring(child(a,1)->tp->size);
    }
    else {
      c=GenLeft(child(a,0),0)||GenRight(child(a,1),1)||"copy t1 t0 "+itostring(child(a,1)->tp->size);
    }
  } 
/*-------------------------------------------------------------------*/
  else if (a->kind=="write" || a->kind=="writeln") {
    if (child(a,0)->kind=="string") {
      //...to be done.
	  c = "wris " + child(a,0)->text;
    } 
    else {//Exp
    	//if(child(a,0)->kind == "(") c = CodeGenInstruction(child(a,0)) ||"wrii t0";
    	//else c=GenRight(child(a,0),0)||"wrii t0";
		c=GenRight(child(a,0),0)||"wrii t0";
    }

    if (a->kind=="writeln") {
      c=c||"wrln";
    }
  }

/*-------------------------------------------------------------------*/
else if(a->kind == "if") {
	int n = newLabelIf();
	if(child(a,2)) {
		c= /*"etiq if_"+itostring(n) ||*/
		GenRight(child(a,0),0)||
		"fjmp t0 else_"+itostring(n) ||
		CodeGenInstruction(child(a,1))||
		"ujmp endif_"+itostring(n)||
		"etiq else_"+itostring(n) ||
		CodeGenInstruction(child(a,2))||
		"etiq endif_"+itostring(n);
	}       
	else {
		c= /*"etiq if_"+itostring(n) ||*/
		GenRight(child(a,0),0)||
		"fjmp t0 endif_"+itostring(n) ||
		CodeGenInstruction(child(a,1))||
		"etiq endif_"+itostring(n);
	}
}

/*-------------------------------------------------------------------*/

else if(a->kind == "while") {
	int n = newLabelWhile();
        c ="etiq while_"+itostring(n) ||
	GenRight(child(a,0),0)||
	"fjmp t0 endwhile_"+itostring(n) ||
	CodeGenInstruction(child(a,1)) ||
	"ujmp while_"+itostring(n) ||
	"etiq endwhile_"+itostring(n);
	
}

/*-------------------------------------------------------------------*/
else if(a->kind == "(") {
	
	AST *aux = child(child(a,1),0);
	//int i = 1;
 	ptype tp = symboltable[child(a,0)->text].tp->down;
 	
 	codechain cpushparam;
 	codechain cremoveparam;
 	
	//if(child(a,0)->tp->kind == "function") c = c || "pushparam 0";
 	
	/*while(aux != NULL) {
		if(aux->kind == "ident"){
			if(tp->kind == "parref")
		 		c = c || "aload _" + aux->text + " t0" || "pushparam t0"; 
		 	else if(tp->kind == "parval")
		 		c = c || "load _" + aux->text + " t0" || "pushparam t0"; 
		 }
		else c = c || GenRight(aux,0) || "pushparam t0"; 
		aux = aux->right;
		tp = tp->right;
		++i;
	}
	c = c || "aload static_link t0" || "pushparam t0" 
		  || "call program_" + child(a,0)->text;
	while (i != 0) {
		c = c || "killparam";
		--i;
	}*/
	//if(child(a,0)->tp->kind == "function") c = c || "popparam t0";
	
	CodeGenRealParams(aux,tp,cpushparam,cremoveparam,0);
	c = c || cpushparam || indirections(symboltable.jumped_scopes(child(a,0)->text),0) || "pushparam t0" 
		|| "call " + symboltable.idtable(child(a,0)->text)+"_" + child(a,0)->text || "killparam"
		|| cremoveparam;
	
}

/*-------------------------------------------------------------------*/

  if(offsetauxspace > maxoffsetauxspace) maxoffsetauxspace = offsetauxspace;



  //cout<<"Ending with node \""<<a->kind<<"\""<<endl;

  return c;
}

void CodeGenSubroutine(AST *a,list<codesubroutine> &l) //Com l'estructura del programa
{
  codesubroutine cs;

  //cout<<"Starting with node \""<<a->kind<<"\""<<endl;
  string idtable=symboltable.idtable(child(a,0)->text);
  cs.name=idtable+"_"+child(a,0)->text;
  symboltable.push(a->sc);
  symboltable.setidtable(idtable+"_"+child(a,0)->text);

  //...to be done.

   AST *aux = child(child(child(a,0),0),0);
//cout << child(child(child(a,0),0),0)->kind << endl;

  /*while(aux != NULL) {

	cout << "_" + child(a,0)->text;
	aux = aux->right;
  }*/

  if(a->tp->kind == "procedure") gencodevariablesandsetsizes(a->sc,cs,0);
  else  gencodevariablesandsetsizes(a->sc,cs,1);
  //gencodevariablesandsetsizes(a->sc,cs);//0 o 1 si es procedure o function
  for (AST *a1=child(child(a,2),0);a1!=NULL;a1=a1->right) {
    CodeGenSubroutine(a1,l);
  }
  /*for(AST* a2 = child(child(a,3),0); a2!=NULL; a2=a2->right) {
//cout << a2->kind<<endl;
	  cs.c = cs.c || CodeGenInstruction(a2) || "retu";
  }*/
  
  newLabelIf(true);
  newLabelWhile(true);
  maxoffsetauxspace=0;

  if(a->tp->kind == "procedure") cs.c = CodeGenInstruction(child(a,3),a->tp->kind) || "retu";
  else if(a->tp->kind == "function") {
  	if(isbasickind(child(a,4)->tp->kind)) {
	  	cs.c = CodeGenInstruction(child(a,3),a->tp->kind) || 
	  	GenRight(child(a,4),0)||
	  	"stor t0 returnvalue" ||
	  	"retu";
	 }
	 else {
	 	cs.c = CodeGenInstruction(child(a,3),a->tp->kind) || 
	  	GenLeft(child(a,4),1)||
	  	"load returnvalue t0" ||
	  	"copy t1 t0 " + itostring(compute_size(child(a,4)->tp)) ||
	  	"retu";
	 }
  }
  
  if (maxoffsetauxspace>0) {
    variable_data vd;
    vd.name="aux_space";
    vd.size=maxoffsetauxspace;
    cs.localvariables.push_back(vd);
  }
  
  symboltable.pop();
  l.push_back(cs);
  //cout<<"Ending with node \""<<a->kind<<"\""<<endl;

}

//  list<codesubroutine>::iterator it1=l.begin();

void CodeGen(AST *a,codeglobal &cg)
{
  initnumops();
  securemodeon();
  cg.mainsub.name="program";
  symboltable.push(a->sc);
  symboltable.setidtable("program");
  gencodevariablesandsetsizes(a->sc,cg.mainsub);
  for (AST *a1=child(child(a,1),0);a1!=0;a1=a1->right) {
    CodeGenSubroutine(a1,cg.l);
  }
  maxoffsetauxspace=0; newLabelIf(true); newLabelWhile(true);
  cg.mainsub.c=CodeGenInstruction(child(a,2))||"stop";
  if (maxoffsetauxspace>0) {
    variable_data vd;
    vd.name="aux_space";
    vd.size=maxoffsetauxspace;
    cg.mainsub.localvariables.push_back(vd);
  }
  symboltable.pop();
}

